package org.jeecg.modules.platformMasterEndPoint.controller;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.hc.mapper.HcMerchantMapper;
import org.jeecg.modules.miniAppCustomerEndPoint.constant.JxRedisConstant;
import org.jeecg.modules.miniAppCustomerEndPoint.entity.JxSiteStoreDo;
import org.jeecg.modules.miniAppCustomerEndPoint.enums.JxOrderStateEnum;
import org.jeecg.modules.miniAppCustomerEndPoint.mapper.JxSiteStoreMapper;
import org.jeecg.modules.platformMasterEndPoint.entity.JxSiteDetailDo;
import org.jeecg.modules.platformMasterEndPoint.entity.JxStoreRepositoryDo;
import org.jeecg.modules.platformMasterEndPoint.mapper.JxSiteDetailMapper;
import org.jeecg.modules.platformMasterEndPoint.mapper.JxStoreRepositoryMapper;
import org.jeecg.modules.platformMasterEndPoint.mapper.JxSummaryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;

@Api(tags = "平台概览")
@RestController
@RequestMapping("/jx/jxSummary")
@Slf4j
public class JxSummaryController {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JxSiteStoreMapper jxSiteStoreMapper;

    @Autowired
    private JxStoreRepositoryMapper jxStoreRepositoryMapper;

    @Resource
    private JxSiteDetailMapper siteMapper;

    @Resource
    private HcMerchantMapper hcMerchantMapper;

    @Autowired
    private JxSummaryMapper jxSummaryMapper;

    @ApiOperation("首页，店铺和站点下拉列表中的数据")
    @GetMapping("/querySiteAndStoreBriefInfo")
    private Result<?> querySiteAndStoreBriefInfo() {

        if (siteMapper == null) {
            siteMapper = SpringContextUtils.getBean(JxSiteDetailMapper.class);
        }
        if (jxSiteStoreMapper == null) {
            jxSiteStoreMapper = SpringContextUtils.getBean(JxSiteStoreMapper.class);
        }
        if (hcMerchantMapper == null) {
            jxStoreRepositoryMapper = SpringContextUtils.getBean(JxStoreRepositoryMapper.class);
        }

        List<JxSiteDetailDo> sites = siteMapper.selectList(new LambdaQueryWrapper<JxSiteDetailDo>().select(JxSiteDetailDo::getId, JxSiteDetailDo::getSiteName));
        HashMap<String, Object> formatedSiteData = new HashMap<>();
        for (int i = 0 ; i < sites.size(); i ++) {
            JxSiteDetailDo site = sites.get(i);

            List<JxSiteStoreDo> stores = jxSiteStoreMapper.selectList(new LambdaQueryWrapper<JxSiteStoreDo>()
                    .eq(JxSiteStoreDo::getSiteId, site.getId()));

            List<JSONArray> storeBriefInfos = new ArrayList<>();
            for (int j = 0 ; j < stores.size() ; j ++) {
                JxSiteStoreDo storeInfo = stores.get(j);
                // todo 修改成从 redis中获取 店铺公共信息
                JxStoreRepositoryDo storeRepositoryDo = jxStoreRepositoryMapper.selectById(storeInfo.getStoreId());
                if (storeRepositoryDo == null) {
                    log.error("无法从店铺信息库表中查找到店铺id为 {} 的店铺信息", storeInfo.getStoreId());
                    continue;
                }
                // 将店铺信息按照二元组的格式返回， ["storeid", "storename"]，每一个 oneStoreBriefInfo 对应一个记录店铺id和名称的 二元组
                JSONArray oneStoreBriefInfo = new JSONArray();
                oneStoreBriefInfo.add(storeInfo.getStoreId());
                oneStoreBriefInfo.add(storeRepositoryDo.getStoreName());

                // [
                //  ["storeId", "storeName"],
                //  ["storeId", "storeName"]
                // ]
                storeBriefInfos.add(oneStoreBriefInfo);
            }
            HashMap<String,Object> siteDetails = new HashMap<>();
            siteDetails.put("siteId", site.getId());
            siteDetails.put("stores", storeBriefInfos);
            formatedSiteData.put(site.getSiteName(), siteDetails);
        }

        return Result.ok(formatedSiteData);
    }

    /**
     * 查询当天用户访问相关的指标
     * 1. 当天pv
     * 2. 当天活跃用户量 DAU
     * 3. 当天下单用户 DU
     *
     * @param startTime 必选参数，格式位于 yyyy-MM-dd
     * @param endTime 必选参数，格式位于 yyyy-MM-dd
     * @param siteId  可选参数
     * @return
     */
    @ApiOperation("首页，查看每天用户访问量")
    @GetMapping("/queryUserAccessSummary")
    public Result<?> queryUserAccessSummary(
            @RequestParam("startTime") String startTime,
            @RequestParam("endTime") String endTime,
            @RequestParam(value = "siteId", required = false) String siteId) {

        // 用户统计的时间范围是跨天的，默认是从前一天的 19:00 到 第二天的 12:00；因此用户的pv会存入到量 key 中
        // 比如 2023-12-25 的小程序访问量，用户可能从 12-24 晚上就会打开小程序点餐，一直持续到 12-25 上午11点，
        // 因此，用户的pv量会统计到 jx_day_views_2023-12-24 和 jx_day_views_2023-12-25 两个key中
        String startTimeYYYYMMDD = getDateTimeRedisKey(startTime, -1);
        String endTimeYYYYMMDD = getDateTimeRedisKey(endTime, 0);

        String redisKey1 = JxRedisConstant.JX_DAY_VIEWS + startTimeYYYYMMDD ;
        String redisKey2 = JxRedisConstant.JX_DAY_VIEWS + endTimeYYYYMMDD ;
        int pageView = 0;
        if (siteId != null) {
            int pv1 = redisUtil.zsetScore(redisKey1, siteId) == null ? 0 : redisUtil.zsetScore(redisKey1, siteId).intValue();
            int pv2 = redisUtil.zsetScore(redisKey2, siteId) == null ? 0 : redisUtil.zsetScore(redisKey2, siteId).intValue();;
            pageView =  pv1 + pv2;
        } else {
            // 对 名称Wie key 的zset进行遍历，每次返回最多100个，从 游标 0 开始，仅返回匹配 * 的所有 member
            // zscan key 0 * 100
            int pv1 = getAllSitePageView(redisKey1);
            int pv2 = getAllSitePageView(redisKey2);
            pageView =  pv1 + pv2;
        }

        HashMap<String, Object> resultData = new HashMap<>();
        if (siteId != null) {
            resultData.put("siteId", siteId);
        } else {
            resultData.put("siteId", "*");
        }
        resultData.put("pageView", pageView); // 当天打开小程序的次数，同一用户多次打开，算作多次
        resultData.put("userView", 0); // 当天下单用户个数
        resultData.put("activeUser", 0); // 当天打开小程序的用户个数，同一用户多次打开，仅计算一次

        return Result.ok(resultData);
    }

    /**
     * zset的 key 为 日期 , jx_day_views_20231225； value为二元组，第一个维度值为 站点id，第二纬度值为 该站点用户的pv值
     * jx_day_views_20231225 --> [[site1, 10], [site2, 8], [site3, 12], [site4, 17], [site5, 9]]
     * @param redisKey
     * @return
     */
    private int getAllSitePageView(String redisKey) {
        int totalPv1 = 0;
        Cursor<ZSetOperations.TypedTuple<Object>> cursor = redisUtil.zSetScan(redisKey, "*", 100);
        while(cursor.hasNext()) {
            ZSetOperations.TypedTuple<Object> setItem = cursor.next();
            int sitePv = setItem.getScore().intValue();
            totalPv1 += sitePv;
        }

        return totalPv1;
    }

    /**
     * 对前端传递的日期参数进行校验和修正
     * @param dateTime
     * @param offset
     * @return
     */
    private String getDateTimeRedisKey(String dateTime, int offset) {
        String yyyyMMdd = "";
        if (dateTime.trim().contains("-")) {
            yyyyMMdd = dateTime.trim();
        } else {
            Calendar nowDate = Calendar.getInstance();
            nowDate.add(Calendar.DATE, offset);
            yyyyMMdd = new SimpleDateFormat("yyyy-MM-dd").format(nowDate.getTime());
            log.error("时间 {} 不是 yyyy-MM-dd格式，默认使用接口调用时间的日期向前退{}天", dateTime, offset);
        }

        return yyyyMMdd;
    }

    @ApiOperation("查看站点订单，营业额")
    @GetMapping("/querySiteSummary")
    public Result<?> querySiteSummary(
            @RequestParam("startTime") String startTime,
            @RequestParam("endTime") String endTime,
            @RequestParam(value = "storeId", required = false) String storeId) {

        HashMap<String,Object> allSiteMetrics = queryMetrics(startTime,endTime, null,storeId);

        HashMap<String, Object> resultData = new HashMap<>();
        resultData.put("totalOrders", allSiteMetrics.get("totalOrders"));
        resultData.put("totalSales", allSiteMetrics.get("totalSales"));
        resultData.put("canceldOrders", allSiteMetrics.get("canceldOrders"));
        resultData.put("canceldSales", allSiteMetrics.get("canceldSales"));
        resultData.put("afterSaleOrders", allSiteMetrics.get("afterSaleOrders"));
        resultData.put("afterSaleSales", allSiteMetrics.get("afterSaleSales"));
        resultData.put("netOrders", allSiteMetrics.get("netOrders"));
        resultData.put("netSales", allSiteMetrics.get("netSales"));

        List<HashMap<String, String>> siteBriefs = jxSummaryMapper.queryDistinctSiteIds(startTime, endTime, storeId);
        HashMap<String, Object> siteMetricMap = new HashMap<>();
        for (int i = 0 ; i < siteBriefs.size() ; i ++){
            HashMap<String, String> siteBrief = siteBriefs.get(i);
            String siteId = siteBrief.get("siteId");
            String siteName = siteBrief.get("siteName");
            if (siteId == null || siteId.isEmpty()) {
                continue;
            }
            HashMap<String,Object> oneSiteMetrics = queryMetrics(startTime,endTime, siteId,storeId);
            siteMetricMap.put(siteName, oneSiteMetrics);
        }

        resultData.put("sites", siteMetricMap);

        return Result.ok(resultData);
    }

    private HashMap<String, Object> queryMetrics(String startTime, String endTime, String siteId, String storeId) {

        int totalOrders = jxSummaryMapper.queryTotalOrders(startTime, endTime,siteId, storeId);
        double totalSales = jxSummaryMapper.queryTotalOrderSales(startTime, endTime,siteId, storeId);

        int canceldOrders = jxSummaryMapper.queryAbnormalOrders(startTime, endTime, siteId, storeId, JxOrderStateEnum.CANCELED.getValue());
        double canceldSales = jxSummaryMapper.queryAbnormalOrderSales(startTime, endTime,siteId,  storeId, JxOrderStateEnum.CANCELED.getValue());

        int afterSaleOrders = jxSummaryMapper.queryAbnormalOrders(startTime, endTime, siteId, storeId, JxOrderStateEnum.AFTERSALE.getValue());
        double afterSaleSales = jxSummaryMapper.queryAbnormalOrderSales(startTime, endTime, siteId, storeId, JxOrderStateEnum.AFTERSALE.getValue());

        int netOrders = totalOrders - canceldOrders - afterSaleOrders;
        double netSales = totalSales - canceldSales - afterSaleSales;

        HashMap<String, Object> metrics = new HashMap<>();
        metrics.put("totalOrders", totalOrders);
        metrics.put("totalSales", totalSales);
        metrics.put("canceldOrders", canceldOrders);
        metrics.put("canceldSales", canceldSales);
        metrics.put("afterSaleOrders", afterSaleOrders);
        metrics.put("afterSaleSales", afterSaleSales);
        metrics.put("netOrders", netOrders);
        metrics.put("netSales", netSales);
        metrics.put("siteId", siteId);

        return metrics;
    }
}
